package msgs

import (
	"encoding/binary"
	"ospf/common"
)

type packetType uint8

const (
	// version is the OSPF version supported by this library (OSPFv2).
	version = 2

	// Fixed length structures. Note that some packets don't have constants
	// here because they only contain trailing variable length data.
	headerLen       = 24
	lsaHeaderLen    = 20
	lsaLen          = 12
	helloPackageLen = 20 // No trailing array of neighbor IDs.
	dataDescLen     = 12 // No trailing array of LSA headers.

	// HelloType is message type value of hello packet in ospf.
	HelloType                packetType = 1
	DatabaseDescription      packetType = 2
	LinkStateRequest         packetType = 3
	LinkStateUpdateType      packetType = 4
	LinkStateAcknowledgement packetType = 5
)

type Packet interface {
	Len() int
	GetHeader() *Header
	// Serialize map packet values to a []byte.
	Serialize() ([]byte, error)
	// Deserialize maps value from data to an ospf packet.
	// data []byte does not contain header information.
	Deserialize([]byte) error
}

// DeserializePacket judge message type, and analyse it.
func DeserializePacket(b []byte) (p Packet, err error) {
	header := Header{}
	err = header.Deserialize(b)
	if err != nil {
		return nil, common.ErrDeserialize("header deserialize.\n" + err.Error())
	}
	switch header.MessageType {
	case HelloType:
		p = &Hello{Header: header}
	case LinkStateUpdateType:
		p = &LinkStateUpdate{Header: header}
	default:
		return nil, common.ErrDeserialize("message type not supported.\n")
	}

	if err = p.Deserialize(b[headerLen:]); err != nil {
		return nil, common.ErrDeserialize("packet deserialize.\n" + err.Error())
	}

	return p, nil
}

// SerializePacket map packet to byte array.
func SerializePacket(p Packet) ([]byte, error) {
	return p.Serialize()
}

func CalculateCheckSum(data []byte) uint16 {
	l := len(data)
	if l%2 != 0 {
		data = append(data, '0')
		l++
	}
	var sum uint32 = 0
	for i := 0; i < l; i += 2 {
		sum += uint32(data[i])<<8 + uint32(data[i+1])
	}
	for sum > 0xffff {
		sum = (sum >> 16) + (sum & 0xffff)
	}
	binary.BigEndian.PutUint16(data[12:14], ^uint16(sum))
	return ^uint16(sum)
}
